using System;
using System.Diagnostics;
using System.IO;
using Agile.Common;
using Agile.Common.Loggers;
using Microsoft.Practices.Unity;

namespace Agile.Genie.Generators
{
    /// <summary>
    /// Summary description for GeneratedFileInfo.
    /// </summary>
    public class GeneratedFileInfo
    {
        private readonly FileInfo _fileInfo;
        private readonly string _generatedStamp;
        private string _fileContents;

        /// <summary>
        /// Initializes a new instance of the GeneratedFileInfo class, which is a command for a FileInfo.
        /// </summary>
        /// <param name="fileName">The fully qualified name of the new file, or the relative file name.</param>
        /// <param name="generatedStamp">string to appear at the top of the file, 
        /// indicating it is a generated file.</param>
        private GeneratedFileInfo(string fileName, string generatedStamp)
        {
            _fileInfo = new FileInfo(fileName);
            _generatedStamp = generatedStamp;
        }

        /// <summary>
        /// Returns true if the file already exists
        /// </summary>
        public bool Exists
        {
            get { return _fileInfo.Exists; }
        }

        /// <summary>
        /// Gets a string representation of the full file path or the file
        /// to be generated.
        /// </summary>
        public string FullFileGenerationPath
        {
            get { return _fileInfo.FullName; }
        }

        /// <summary>
        /// Returns true if the existing file contains the GeneratedStamp
        /// </summary>
        public bool HasGeneratedStamp
        {
            get
            {
                Debug.Write(string.Format("HasGeneratedStamp: FileContents:{0}; Stamp:{1}", FileContents,
                                          _generatedStamp));
                if (Exists)
                    return Strings.Contains(FileContents, _generatedStamp);
                return false;
            }
        }

        /// <summary>
        /// Gets the entire contents of the file as a string.
        /// NOTE: Returns null if the file does NOT exist.
        /// </summary>
        /// <remarks>Only tested for text only file types!</remarks>
        public string FileContents
        {
            get
            {
                if (_fileContents == null)
                    LoadFileContents();
                return _fileContents;
            }
        }

        /// <summary>
        /// Initializes a new instance of the GeneratedFileInfo class, which is a command for a FileInfo.
        /// </summary>
        /// <param name="fileName">The fully qualified name of the new file, or the relative file name.</param>
        /// <param name="generatedStamp">string to appear at the top of the file, 
        /// indicating it is a generated file.</param>
        public static GeneratedFileInfo Build(string fileName, string generatedStamp)
        {
            return new GeneratedFileInfo(fileName, generatedStamp);
        }

        /// <summary>
        /// Load the contents of the the existing file
        /// </summary>
        private void LoadFileContents()
        {
            // Dont bother trying to load the contents if the file does not exist.
            if (!Exists) return;

            FileStream fs;
            fs = new FileStream(_fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.Read);

            var sr = new StreamReader(fs);
            // Set the file contents member to the contents of the FileStream.
            _fileContents = sr.ReadToEnd();
            // Close the stream and reader
            sr.Close();
            fs.Close();
        }

        /// <summary>
        /// Write the given details to the specified file.
        /// </summary>
        /// <param name="newFileContents">The contents of the file</param>
        /// <remarks>Raises a 'CreateGeneratedFileError' event if
        /// there is a problem creating the file.</remarks>
        public void WriteToFile(string newFileContents)
        {
            //determine what to do
            CreateProjectDirectoryIfDoesntExist();

            try
            {
                // Create a file if doesn't exist, write to it.
                using (StreamWriter sw = _fileInfo.CreateText())
                {
                    sw.Write(newFileContents);
                    sw.Close();

                    // finally set the local variable to the new contents
                    _fileContents = newFileContents;
                    _fileInfo.Refresh();
                }
                Logger.Write(string.Format("Generated code written to: {0}", _fileInfo.FullName), "Trace");
            }
            catch (Exception ex)
            {
                Logger.Write(
                    string.Format("Error {0} writing generated code to: {1}.", ex.Message, _fileInfo.FullName), "Trace");
                if (CreateGeneratedFileError != null)
                    CreateGeneratedFileError(this, ex.Message);
            }
        }

        /// <summary>
        /// Checks if the directory exists and creates it if it doesn't
        /// </summary>
        private void CreateProjectDirectoryIfDoesntExist()
        {
            if (!Directory.Exists(_fileInfo.DirectoryName))
            {
                _fileInfo.Directory.Create();
                Logger.Write(string.Format("Creating directory: '{0}'\n", _fileInfo.DirectoryName), "Trace");
            }
        }

        /// <summary>
        /// Empties the contents of the file (i.e. sets it to an empty string)
        /// </summary>
        public void FlushContents()
        {
            Debug.Write("FlushContents invoked.\n");
            WriteToFile(string.Empty);
        }

        #region Events

        #region Delegates

        /// <summary>
        /// Delegate for handling when we are unable to generate the generated file
        /// </summary>
        public delegate void GeneratedFileCreationErrorHandler(GeneratedFileInfo generatedFileInfo, string errorMessage);

        #endregion

        /// <summary>
        /// Event handler for when there was a problem creating 
        /// the generated file.
        /// </summary>
        public event GeneratedFileCreationErrorHandler CreateGeneratedFileError;

        #endregion

        // set the default logger
        private ILogger logger = new DebugLogger();

        [Dependency]
        public ILogger Logger
        {
            get { return logger; }
            set { logger = value; }
        }

    }
}